<template>
  <div style="display: flex; height: 100vh">
    <!-- 控制面板 -->
    <div
      style="
        width: 300px;
        background: #222;
        color: #fff;
        padding: 20px;
        overflow-y: auto;
      "
    >
      <h3>光源类型实验</h3>
      <el-form label-width="90px">
        <el-form-item label="环境光">
          <el-switch v-model="ambientOn" @change="toggleLights" /> 强度
          <el-slider
            v-model="ambientIntensity"
            :min="0"
            :max="2"
            :step="0.01"
            style="width: 120px"
            @input="toggleLights"
          />
        </el-form-item>
        <el-form-item label="平行光">
          <el-switch v-model="dirOn" @change="toggleLights" /> 强度
          <el-slider
            v-model="dirIntensity"
            :min="0"
            :max="2"
            :step="0.01"
            style="width: 120px"
            @input="toggleLights"
          />
        </el-form-item>
        <el-form-item label="点光源">
          <el-switch v-model="pointOn" @change="toggleLights" /> 强度
          <el-slider
            v-model="pointIntensity"
            :min="0"
            :max="3"
            :step="0.01"
            style="width: 120px"
            @input="toggleLights"
          />
        </el-form-item>
        <el-form-item label="聚光灯">
          <el-switch v-model="spotOn" @change="toggleLights" /> 强度
          <el-slider
            v-model="spotIntensity"
            :min="0"
            :max="3"
            :step="0.01"
            style="width: 120px"
            @input="toggleLights"
          />
        </el-form-item>
        <el-form-item label="半球光">
          <el-switch v-model="hemiOn" @change="toggleLights" /> 强度
          <el-slider
            v-model="hemiIntensity"
            :min="0"
            :max="3"
            :step="0.01"
            style="width: 120px"
            @input="toggleLights"
          />
        </el-form-item>
        <el-form-item label="区域光">
          <el-switch v-model="rectOn" @change="toggleLights" /> 强度
          <el-slider
            v-model="rectIntensity"
            :min="0"
            :max="10"
            :step="0.01"
            style="width: 120px"
            @input="toggleLights"
          />
        </el-form-item>
      </el-form>
      <div style="font-size: 12px; color: #ccc; margin-top: 12px">
        各种光源可自由开关/调节强度，模型材质采用 Standard 支持大多数效果。
      </div>
    </div>
    <!-- three.js 画布 -->
    <div ref="threeBox" style="flex: 1; background: #222"></div>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { RectAreaLightUniformsLib } from "three/examples/jsm/lights/RectAreaLightUniformsLib";

const threeBox = ref(null);
let renderer, scene, camera, controls, mesh, animateId;

// 光源对象定义（后面要动态增减）
let ambientLight, dirLight, pointLight, spotLight, hemiLight, rectLight;

// 光源可调参数
const ambientOn = ref(true);
const ambientIntensity = ref(0.5);

const dirOn = ref(true);
const dirIntensity = ref(1);

const pointOn = ref(false);
const pointIntensity = ref(1);

const spotOn = ref(false);
const spotIntensity = ref(1);

const hemiOn = ref(false);
const hemiIntensity = ref(1);

const rectOn = ref(false);
const rectIntensity = ref(2);

// 初始化
onMounted(() => {
  RectAreaLightUniformsLib.init();
  // 场景
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0x232323);

  // 相机
  camera = new THREE.PerspectiveCamera(
    60,
    threeBox.value.clientWidth / threeBox.value.clientHeight,
    0.1,
    100
  );
  camera.position.set(3, 2, 6);

  // 渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(threeBox.value.clientWidth, threeBox.value.clientHeight);
  renderer.shadowMap.enabled = true;
  threeBox.value.appendChild(renderer.domElement);

  // 控制器
  controls = new OrbitControls(camera, renderer.domElement);

  // 添加一个地面
  const groundGeo = new THREE.PlaneGeometry(10, 10);
  const groundMat = new THREE.MeshStandardMaterial({
    color: 0x888888,
    roughness: 0.8,
  });
  const ground = new THREE.Mesh(groundGeo, groundMat);
  ground.rotation.x = -Math.PI / 2;
  ground.position.y = -1;
  ground.receiveShadow = true;
  scene.add(ground);

  // 添加一个带细分的球体，便于观察光照细节
  const sphereGeo = new THREE.SphereGeometry(1, 48, 48);
  const sphereMat = new THREE.MeshStandardMaterial({
    color: 0x3399ff,
    metalness: 0.3,
    roughness: 0.6,
  });
  mesh = new THREE.Mesh(sphereGeo, sphereMat);
  mesh.position.y = 1;
  mesh.castShadow = true;
  mesh.receiveShadow = true;
  scene.add(mesh);

  // 加个参考轴
  //   scene.add(new THREE.AxesHelper(2));

  addAllLights();

  animate();
});

onBeforeUnmount(() => {
  cancelAnimationFrame(animateId);
  renderer?.dispose?.();
});

// 光源创建和管理
function addAllLights() {
  // 环境光, 全场景均匀照亮，没有方向性，不产生阴影。可以让场景“整体不发黑”，但物体缺乏立体感。
  ambientLight = new THREE.AmbientLight(0xffffff, ambientIntensity.value);
  scene.add(ambientLight);

  // 平行光, 像太阳光一样的“方向光”，所有光线平行照射，可产生清晰阴影，光源没有衰减。
  dirLight = new THREE.DirectionalLight(0xffffff, dirIntensity.value);
  dirLight.position.set(4, 8, 4);
  dirLight.castShadow = true;
  scene.add(dirLight);
  // DirectionalLightHelper 平行光辅助器,平行光没有固定起点，只是一个方向
  scene.add(new THREE.DirectionalLightHelper(dirLight, 0.3));

  // 点光源, 像灯泡一样的“球面辐射”光源，从一个点向四周发射，强度随距离衰减（有距离参数）。
  pointLight = new THREE.PointLight(0xffaaff, pointIntensity.value, 20);
  pointLight.position.set(-2, 4, 3);
  pointLight.castShadow = true;
  scene.add(pointLight);
  // PointLightHelper 点光源辅助器，起点是点光源的位置
  scene.add(new THREE.PointLightHelper(pointLight, 0.2));

  // 聚光灯, 像手电筒/舞台灯一样，发射一个“锥形光束”，有方向、有角度，可以聚焦一块区域，也能投影出锥形阴影。
  spotLight = new THREE.SpotLight(
    0x00ffcc,
    spotIntensity.value,
    30,
    Math.PI / 7,
    0.2,
    1.5
  );
  spotLight.position.set(3, 7, 5);
  spotLight.castShadow = true;
  scene.add(spotLight);
  // new THREE.SpotLightHelper 聚光灯 SpotLight 的辅助器 辅助器的起点：通常是 SpotLight 的位置，线条穿过场景中的物体。
  scene.add(new THREE.SpotLightHelper(spotLight));

  // 半球光 模拟天空光和地面反光的渐变，天空（上方）照亮物体上表面，地面色影响下表面，常见于真实世界的户外场景。
  hemiLight = new THREE.HemisphereLight(
    0x87ceeb,
    0x444444,
    hemiIntensity.value
  );
  hemiLight.position.set(0, 8, 0);
  scene.add(hemiLight);

  // 区域光 矩形发光面，能模拟窗户/广告牌/大型面光源，只有 Standard/Physical 材质支持。适合写实渲染，不支持阴影（Three.js 支持有限）。
  rectLight = new THREE.RectAreaLight(0xffffff, rectIntensity.value, 4, 2);
  rectLight.position.set(0, 5, -4);
  rectLight.lookAt(0, 0, 0);
  scene.add(rectLight);
  // 可以用 RectAreaLightHelper 但需要引入 helper
}

// 切换光源开关与强度
function toggleLights() {
  ambientLight.intensity = ambientOn.value ? ambientIntensity.value : 0;
  dirLight.intensity = dirOn.value ? dirIntensity.value : 0;
  pointLight.intensity = pointOn.value ? pointIntensity.value : 0;
  spotLight.intensity = spotOn.value ? spotIntensity.value : 0;
  hemiLight.intensity = hemiOn.value ? hemiIntensity.value : 0;
  rectLight.intensity = rectOn.value ? rectIntensity.value : 0;
}

watch(
  [
    ambientOn,
    ambientIntensity,
    dirOn,
    dirIntensity,
    pointOn,
    pointIntensity,
    spotOn,
    spotIntensity,
    hemiOn,
    hemiIntensity,
    rectOn,
    rectIntensity,
  ],
  toggleLights
);

function animate() {
  animateId = requestAnimationFrame(animate);
  mesh.rotation.y += 0.003;
  controls.update();
  renderer.render(scene, camera);
}
</script>
